<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<HTML>
<HEAD>
	<title>3D-Model of Moscow Metro</title>
	<meta charset="utf-8">
	<meta content="3D-Model of Moscow Metro, three-dimensional subway scheme" name="keywords">
    
    
    
    
	<meta property="og:image" content="http://varf.ru/metro3d/style/screenshot2.png"/>
	<link href="https://analytics.emiia.ai/metro/metro.css" rel="stylesheet" type="text/css">    
	<script type="text/javascript" src="https://analytics.emiia.ai/metro/metro.min.js"></script>
    
    
	<script type="text/javascript" src="https://analytics.emiia.ai/metro/data.js"></script>
	<script type="text/javascript" src="https://analytics.emiia.ai/metro/throttle.js"></script>
    	<script type="text/javascript" src="https://analytics.emiia.ai/tooltip.js"></script>

    
    
    
    
    

        
	<script type="text/javascript" src="https://analytics.emiia.ai/metro/animate.js"></script>
    
	<script type="text/javascript" src="https://analytics.emiia.ai/metro/get-params.js"></script>
    
	<script type="text/javascript" src="https://analytics.emiia.ai/metro/get-css.js"></script>

    
    

    
    
    
    
    
	<script type="text/javascript" src="https://analytics.emiia.ai/metro/mouse-motion-controller.js"></script>
    
    
    
	<link href="https://analytics.emiia.ai/metro/tooltip.css" rel="stylesheet" type="text/css">
	<link href="https://analytics.emiia.ai/metro/night.css" rel="stylesheet" type="text/css">
    
    
    

   
	


	
	
</HEAD>
<BODY>
		<div id="lang">
			<a id="ru" href="/">RU</a>
			<span>EN</span>
		</div>
	<h1>3D-Model of Moscow Metro</h1>
	
	<h2><span data-tooltip="For 96-dpi displays">Scale<sup>*</sup></span>: 1 cm = <span id="scale_coord">12.3 km</span> (horizontal), <span id="scale_depth">8 m</span> (depth)</h2>
		
	<div id="metro" class="noAnimation">
		<a name="metro"></a>
		
		<a class="button" id="up" href="#metro"><span data-tooltip="Rotate by the mouse with the left button pressed">&uarr;</span></a>
		<a class="button" id="down" href="#metro"><span data-tooltip="Rotate by the mouse with the left button pressed">&darr;</span></a>
		<a class="button" id="left" href="#metro"><span data-tooltip="Rotate by the mouse with the left button pressed">&larr;</span></a>
		<a class="button" id="right" href="#metro"><span data-tooltip="Rotate by the mouse with the left button pressed">&rarr;</span></a>
		<a class="button" id="plus" href="#metro"><span data-tooltip="Scale by the mouse with the right button pressed">+</span></a>
		<a class="button" id="minus" href="#metro"><span data-tooltip="Scale by the mouse with the right button pressed">&minus;</span></a>
		<a class="button" id="flat" href="#metro"><span data-tooltip="Set view from above"><span data-tooltip="Set view from above" style="display:inline; margin-left:7px">&#8597;</span><span data-tooltip="Set view from above" style="display:inline; margin-left:-22px">&#8596;</span></span></a>
		
		<div id="select-year">
			<h2>Year:</h2>
			<select id="year">
			</select>
		</div>
		
		<div id="switches">
			<a id="captions" href="#">Captions: ON</a>
			<a id="night" href="#">Light: ON</a>
			<a id="animation" href="#">Animation: ON</a>
			<a id="scroll" href="#details">More&nbsp;<b>&darr;</b></a>
		</div>
		
		<div class="ad-banner" id="ad-metro-top">
			<!-- Yandex.RTB R-A-328107-3 -->
			<div id="yandex_rtb_R-A-328107-3"></div>
			<script type="text/javascript">
				(function(w, d, n, s, t) {
					w[n] = w[n] || [];
					w[n].push(function() {
						Ya.Context.AdvManager.render({
							blockId: "R-A-328107-3",
							renderTo: "yandex_rtb_R-A-328107-3",
							async: true
						});
					});
					t = d.getElementsByTagName("script")[0];
					s = d.createElement("script");
					s.type = "text/javascript";
					s.src = "//an.yandex.ru/system/context.js";
					s.async = true;
					t.parentNode.insertBefore(s, t);
				})(this, this.document, "yandexContextAsyncCallbacks");
			</script>
			
			<div class="ad-banner-close"></div>
		</div>
		
		<div class="ad-banner" id="ad-metro-bottom">
			<!-- Yandex.RTB R-A-328107-1 -->
			<div id="yandex_rtb_R-A-328107-1"></div>
			<script type="text/javascript">
				(function(w, d, n, s, t) {
					w[n] = w[n] || [];
					w[n].push(function() {
						Ya.Context.AdvManager.render({
							blockId: "R-A-328107-1",
							renderTo: "yandex_rtb_R-A-328107-1",
							async: true
						});
					});
					t = d.getElementsByTagName("script")[0];
					s = d.createElement("script");
					s.type = "text/javascript";
					s.src = "//an.yandex.ru/system/context.js";
					s.async = true;
					t.parentNode.insertBefore(s, t);
				})(this, this.document, "yandexContextAsyncCallbacks");
			</script>
			
			<div class="ad-banner-close" data-linked-banners="ad-column-center"></div>
		</div>
	</div>
	
	<script type="text/javascript">
		// Создание метро:		
		var MoscowMetro = new Metro('metro');
		MoscowMetro.inputGetParams();						// ввод GET-параметров
		for (var i=0; i<data.CoordinatesList.length; i++)	// создание линий, станций и тоннелей
			MoscowMetro.addLine(data.CoordinatesList[i], data.NamesList[i], data.ClassesList[i]);
		MoscowMetro.setInterchanges(data.Interchanges);		// создание пересадок
		MoscowMetro.applyFilter();							// применение фильтра к станциям и тоннелям
		
		// Заполнение списка лет:
		var years = document.getElementById('year');
		var currentYear = (new Date()).getFullYear();
		var selectedYear = MoscowMetro.filter.getSelectedYear();
		for (var i=currentYear; i>=1935; i--) {
			var option = document.createElement('option');
			option.text = i;
			option.value = i;
			if (i == selectedYear) option.selected = true;
			years.options.add(option);
		}
		// Изолирование выпадающего списка от события нажатия мыши:
		years.onmousedown = function(e) {
			if (!e) e = window.event;
			if (e.stopPropagation) e.stopPropagation();
			else e.cancelBubble = true;		
		}

		// Первая отрисовка:
		var startDraw = new Date;
		MoscowMetro.outputGetParams();
		MoscowMetro.draw();
		showScale();
		
		// Действия мышью:
		var MetroRotation = new MouseMotionController(document.getElementById('metro'));
		// Вращение:
		MetroRotation.actionLeft = function(x1,y1, x2,y2) {
			MoscowMetro.rotate(
				-180*(x2-x1)/MoscowMetro.elemMetro.offsetWidth,
				180*(y2-y1)/MoscowMetro.elemMetro.offsetHeight
			);
		}
		// Масштабирование:
		MetroRotation.actionRight = function(x1,y1, x2,y2) {
			MoscowMetro.zoom(1+(y2-y1)/MoscowMetro.elemMetro.offsetHeight);
			showScale();
		}
		// Выбор веток:
		MetroRotation.actionClick = function(e) {
			// Получение объекта события и цели:
			var event = e || window.event;
			var target = event.target || event.srcElement;
			// Клик на станции либо туннеле - выбор ветки:
			if ((target.tagName == 'LI' || target.tagName == 'SPAN') && target.parentNode.tagName == 'UL') {
				var ULs = this.getElementsByTagName('UL');
				var trans = '0.2';
				// Обычный выбор:
				if (!event.ctrlKey) {
					for (var i=0; i<ULs.length; i++)
						if (ULs[i] != target.parentNode) ULs[i].style.opacity = trans;
					target.parentNode.style.opacity = '1';
				}
				// Ctrl-выбор:
				else {
					if (target.parentNode.style.opacity == '1') target.parentNode.style.opacity = trans;
					else target.parentNode.style.opacity = '1';
				}
			}
			// Клик в пустоте - снятие выбора веток:
			if (target == this) {
				var ULs = this.getElementsByTagName('UL');
				for (var i=0; i<ULs.length; i++)
					ULs[i].style.opacity = '1';
			}	
		}
		// Обновление GET-параметров:
		MetroRotation.actionUp = function(e) {
			MoscowMetro.outputGetParams();
		}
				
		// Управление кнопками:
		var dAngle = 15;						// Приращение угла поворота (градусы)
		var factorDistance = 1.1;				// Увеличение расстояния обзора (множитель)
		var animationDuration = 500;			// Длительность анимации (задержка между вызовами управления, мс)
		var frameDelay = new Date - startDraw;	// Интервал одного кадра анимации, мс
		
		// Вращение вверх:
		document.getElementById('up').onclick = throttle(
			function() {
				animate({
					duration: animationDuration,
					delay: frameDelay,
					delta: makeEaseInOut(quad),
					from: MoscowMetro.camera.getAngles(),
					step: function(progress) {
						MoscowMetro.camera.setAngles(this.from.phi, Number(this.from.theta) - Number(progress*dAngle));
						MoscowMetro.draw();
					},
					complete: MoscowMetro.outputGetParams
				});
			},
			animationDuration, 
			true
		);
		
		// Вращение вниз:
		document.getElementById('down').onclick = throttle(
			function() {
				animate({
					duration: animationDuration,
					delay: frameDelay,
					delta: makeEaseInOut(quad),
					from: MoscowMetro.camera.getAngles(),
					step: function(progress) {
						MoscowMetro.camera.setAngles(this.from.phi, Number(this.from.theta) + Number(progress*dAngle));
						MoscowMetro.draw();
					},
					complete: MoscowMetro.outputGetParams
				});
			},
			animationDuration, 
			true
		);
		
		// Вращение влево:
		document.getElementById('left').onclick = throttle(
			function() {
				animate({
					duration: animationDuration,
					delay: frameDelay,
					delta: makeEaseInOut(quad),
					from: MoscowMetro.camera.getAngles(),
					step: function(progress) {
						MoscowMetro.camera.setAngles(Number(this.from.phi) + Number(progress*dAngle), this.from.theta);
						MoscowMetro.draw();
					},
					complete: MoscowMetro.outputGetParams
				});
			},
			animationDuration, 
			true
		);

		// Вращение вправо:
		document.getElementById('right').onclick = throttle(
			function() {
				animate({
					duration: animationDuration,
					delay: frameDelay,
					delta: makeEaseInOut(quad),
					from: MoscowMetro.camera.getAngles(),
					step: function(progress) {
						MoscowMetro.camera.setAngles(Number(this.from.phi) - Number(progress*dAngle), this.from.theta);
						MoscowMetro.draw();
					},
					complete: MoscowMetro.outputGetParams
				});
			},
			animationDuration, 
			true
		);

		// Приближение:
		document.getElementById('plus').onclick = throttle(
			function() {
				animate({
					duration: animationDuration,
					delay: frameDelay,
					delta: makeEaseInOut(quad),
					from: MoscowMetro.camera.getViewDistance(),
					step: function(progress) {
						MoscowMetro.camera.setViewDistance(this.from*(1 - progress*(1-1/factorDistance)));
						MoscowMetro.draw();
						showScale();
					},
					complete: MoscowMetro.outputGetParams
				});
			},
			animationDuration, 
			true
		);

		// Удаление:
		document.getElementById('minus').onclick = throttle(
			function() {
				animate({
					duration: animationDuration,
					delay: frameDelay,
					delta: makeEaseInOut(quad),
					from: MoscowMetro.camera.getViewDistance(),
					step: function(progress) {
						MoscowMetro.camera.setViewDistance(this.from*(1 + progress*(factorDistance-1)));
						MoscowMetro.draw();
						showScale();
					},
					complete: MoscowMetro.outputGetParams
				});
			},
			animationDuration, 
			true
		);

		// Вид сверху:
		document.getElementById('flat').onclick = throttle(
			function() {
				animate({
					duration: animationDuration,
					delay: frameDelay,
					delta: makeEaseInOut(quad),
					from: MoscowMetro.camera.getAngles(),
					step: function(progress) {
						MoscowMetro.camera.setAngles(
							Number(this.from.phi)   + Number(progress*(-90-this.from.phi)), 
							Number(this.from.theta) + Number(progress*( 90-this.from.theta))
						);
						MoscowMetro.draw();
					},
					complete: MoscowMetro.outputGetParams
				});
			},
			animationDuration, 
			true
		);
		
		// Переключатель русского языка:
		document.getElementById('ru').onclick = function() {
			this.href = writeGetParams({en: ''}, {hardcode: true, noHash: true});
			return true;
		}
		// Переключатель убрать/показать названия:
		document.getElementById('captions').onclick = function() { 
			classOnOff(this, MoscowMetro.elemMetro, 'noCaptions', 'Captions: ON', 'Captions: OFF'); 
			return false; 
		};
		
		// Переключатель убрать/показать анимацию (только для управления кнопками):
		document.getElementById('animation').onclick = function() { 
			if (animationDuration > 0) {
				this.setAttribute('data-animation-duration', animationDuration);
				animationDuration = 0;
				this.innerHTML = 'Animation: OFF';
			}
			else {
				animationDuration = parseInt(this.getAttribute('data-animation-duration'));
				if (isNaN(animationDuration)) animationDuration = 0;
				this.innerHTML = 'Animation: ON';
			}
			return false; 
		};
		
		// Переключатель убрать/показать названия:
		document.getElementById('night').onclick = function() { 
			classOnOff(this, document.body, 'night', 'Light: ON', 'Light: OFF'); 
			return false; 
		};
		
		// Кнопки закрытия рекламы:
		var adBannerClosers = document.getElementsByClassName('ad-banner-close');
		for (var i=0; i < adBannerClosers.length; i++) { 
			adBannerClosers[i].onclick = function() {
				// Закрытие связанных баннеров, если они есть:
				var linkedBanners = this.getAttribute('data-linked-banners');
				if (linkedBanners) {
					linkedBanners = linkedBanners.split(' ');
					for (var k=0; k < linkedBanners.length; k++)  {
						var adBanner = document.getElementById(linkedBanners[k]);
						if (adBanner) adBanner.parentNode.removeChild(adBanner);
					}
				}
				// Закрытие родительского баннера:
				var adBanner = this.parentNode;
				if (adBanner) adBanner.parentNode.removeChild(adBanner);
			}
		}
					
		// Автоматическое закрытие рекламы, если она не показывается: 
		var adBanners = document.getElementsByClassName('ad-banner');
		for (var i=0; i < adBanners.length; i++)
			setTimeout(function(adBanner) {
				if (parseInt(getComputedStyle(adBanner).height) == 0)
					adBanner.parentNode.removeChild(adBanner);
			}, 10000, adBanners[i]);

		// Выбор года:
		document.getElementById('year').onchange = function() {
			MoscowMetro.filter.setSelectedYear(this.value);
			MoscowMetro.applyFilter();
			MoscowMetro.outputGetParams();
			MoscowMetro.draw();
		}
		document.getElementById('year').onkeyup = document.getElementById('year').onchange;
		
		// Изменение размеров окна:
		window.onresize = function() { 
			MoscowMetro.updateSize(); 
			MoscowMetro.draw(); 
			showScale(); 
		};
		
		// Вывод масштаба:
		function showScale() {
			var destCoord = document.getElementById('scale_coord');
			var destDepth = document.getElementById('scale_depth');
			var scale = MoscowMetro.camera.getMetersPerCm(96);
			destCoord.innerHTML = Math.round(scale[0]) + ' m';
			destDepth.innerHTML = Math.round(scale[1]*10)/10 + ' m';
		}
		
		// Добавление/удаление класса с помощью переключателя:
		function classOnOff(switchElem, targetElem, className, onText, offText) {
			if (switchElem.innerHTML == onText) {
				targetElem.className += ' ' + className;
				switchElem.innerHTML = offText; 
			}
			else {
				targetElem.className = targetElem.className.split(className).join('');
				switchElem.innerHTML = onText; 
			}
		}
	</script>
	

    
 			</div>
		</div>
		
	</div>
    




</BODY>
</HTML> 
